熟悉题目
启动脚本,是qwb设备
1 | #!/bin/bash |
一开始在16.04,18.04上尝试启动,结果安装的依赖库好像版本不太符合要求,最终在19.04上面安装依赖库即可启动了,看来强网杯还是紧跟最新的系统啊
tips:缺少库可以用 apt-cache search 去查找后安装
启动起来,先看pci设备
1 | / # lspci |
再看看qwb_class_init
函数,可以确定是00:04.0 Class 00ff: 1234:8848
1 | void __fastcall qwb_class_init(ObjectClass_0 *a1, void *data) |
漏洞代码分析
这个很多多线程的锁与解锁,所以还是比较难看的
qwb_mmio_read
当addr为0-10的功能,其他情况继续看下面
1 | 0、初始化crypt_key,input_buf,output_buf(crypto.statu的最低的1,3位为1就什么也不做) |
代码太长就只贴default的代码,下面把lock和unlock的代码去掉了
1 | default: |
功能如下,根据addr的值进行选择:
1 | 0x1000-0x1fff: size为1,而且addr< strlen((const char *)opaque->crypto.crypt_key) + 0x1000,才能进入下一步,下一步要opaque->crypto.statu == 4,最终才读取*((_BYTE *)opaque + addr - 0x5C0)的值 |
此外,还有重要的是,我们调用了下面的才能进入addr大于0x3000的分支
1 | qwb_decrypt_processing_thread将status设置为8 |
qwb_mmio_write的话如下,没什么漏洞,只是设置crypt_key和input_buf
1 | void __fastcall qwb_mmio_write(QwbState *opaque, hwaddr addr, uint64_t val, unsigned int size) |
那么漏洞是什么?
1、qwb_mmio_read在填满output_buf的时候,strlen会大于0x800,在读取output_buf的时候,可以越界读,读取到函数指针
2、还有就是在aes_encrypt_function
和aes_decrypto_function
中,以aes_encrypt_function
为例,最后会各种异或操作一波,由于v7最多可以到0x800,最终将溢出output_buf,溢出8个字节,可以覆盖encrypt_function指针
1 | *(_QWORD *)&output_buf[v7] = v25; |
漏洞利用
保护措施,全开
1 | root@ubuntu:~/qemu_escape/qwb-preliminary-2019-qwct# checksec ./qe* |
利用思路:
1、mmio_write不能直接填充output_buf,所以我们通过调用stream_encrypto_fucntion去填充疑惑后都是非0的,那么strlen计算就会超过0x800,那就可以越界读,读取到函数指针stream_encrypto_fucntion的地址,从而算出程序的基址,及system_plt地址
2、虽然aes_encrypt_function和aes_decrypto_function都有8字节溢出,但是我们需要控制output_buf的值,我们才能最终控制计算出来的值(即循环异或,第一次是异或0,第二次是异或上一次的结果),直接通过aes_encrypt_function利用有点困哪,我们难以控制加密后的值。但是我们先aes加密,再通过aes解密,那么我们就可以精准控制解密结果了。
1 | //下面是aes_decrypto_function的部分代码 |
最终exp
1 | // -*- coding: utf-8 -*- |
最终效果:
cat flag:
弹计算器:
参考
https://github.com/ray-cp/vm-escape/tree/master/qemu-escape/qwb-preliminary-2019-qwct